﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;
using Facebook.Api;

namespace Facebook
{
    [Serializable]
    public class FacebookResponse<TValue>
    {
        internal FacebookResponse(Exception ex)
        {
            this.ResponseException = ex;
        }

        internal FacebookResponse(String content, String responseXmlRoot)
        {
            var responseContent = XDocument.Parse(content.Replace("xmlns=\"http://api.facebook.com/1.0/\"", String.Empty));
            try
            {
                if (FacebookApiException.IsApiException(responseContent.Root)) throw new FacebookApiException(responseContent.Root);
                else
                {
                    Type type = typeof(TValue);

                    if (type.IsValueType || type == typeof(String))
                    {
                        var result = responseContent.XPathSelectElement("//" + responseXmlRoot).Value;
                        if (type == typeof(String)) this.Value = (TValue)(Object)result;
                        else
                        {
                            Object value = null;
                            if (type.IsIntegral()) value = Int64.Parse(result);
                            else value = result;
                            
                            this.Value = (TValue)Convert.ChangeType(value, type);
                        }
                    }
                    else
                    {
                        var typeInterfaces = type.GetInterfaces();
                        if (typeInterfaces.Contains(typeof(IEnumerable)))
                        {
                            if (!typeInterfaces.Any(i => i.Name == "IFacebookList")) throw new InvalidOperationException("Collections must be a subclass of FacebookList<T>.");

                            var listType = type.GetGenericArguments()[0];
                            var objAttr = (FacebookObjectAttribute)listType.GetCustomAttributes(typeof(FacebookObjectAttribute), true)[0];

                            var ctor = type.GetConstructor(new Type[] { typeof(XElement), typeof(String) });
                            var result = ctor.Invoke(new Object[] { responseContent.Root, objAttr.XPath });
                            this.Value = (TValue)result;
                        }
                        else if (typeInterfaces.Contains(typeof(IFacebookObject)))
                        {
                            var ctor = type.GetConstructor(new Type[] { typeof(XElement) });
                            var result = ctor.Invoke(new Object[] { responseContent.Root });
                            this.Value = (TValue)result;
                        }
                        else throw new ArgumentException(String.Format("{0} must be a String, Value type, or a subclass FacebookList<T> or an implementation of IFacebookObject.", type.FullName), "TValue");
                    }
                }
            }
            catch (Exception ex)
            {
                this.ResponseException = ex;
            }
        }

        public Exception ResponseException { get; private set; }

        public Boolean IsError { get { return this.ResponseException != null; } }

        public TValue ReturnOrHandle(IFacebookContext facebookContext)
        {
            if (this.IsError)
            {
                facebookContext.HandleException(this.ResponseException);
                return default(TValue);
            }
            else return this.Value;
        }

        public TValue Value { get; private set; }
    }
}